package puzzle.hashikake.solver;

import java.util.ArrayList;
import java.util.List;

import puzzle.common.data.HatanException;
import puzzle.common.solver.AbstractSolver;
import puzzle.common.solver.BoardSnapshot;
import puzzle.common.solver.SolutionState;
import puzzle.hashikake.data.HashiState;
import puzzle.hashikake.data.HashikakeBoard;
import puzzle.hashikake.data.ShimaDef;

public class HashikakeSolver extends
		AbstractSolver<HashikakeBoard, HashikakeTopology, HashikakeAssumption> {

	public HashikakeSolver() {
		addRule(new NumberRule());

		addInitialRule(new OneOneRule());
		addInitialRule(new TwoTwoRule());
	}

	@Override
	protected HashikakeTopology analyzeTopology(HashikakeBoard b) {
		HashikakeTopologyAnalyzer z = new HashikakeTopologyAnalyzer(b);
		return z.analyze();
	}

	@Override
	protected SolutionState checkSolutionState(HashikakeBoard b, HashikakeTopology topology) {
		boolean isComplete = true;

		if (topology.hasClosedNetwork() && topology.getNumberOfNetworks() > 1) {
			return SolutionState.HATAN;
		}

		for (ShimaDef shima : b.getShimaList()) {
			int hashiNumber = 0;
			for (HashiState s : b.getHashiStatesForShima(shima)) {
				hashiNumber += s.isFixed() ? s.getNumber() : 0;
			}
			if (hashiNumber < shima.n) {
				isComplete = false;
			} else if (hashiNumber > shima.n) {
				return SolutionState.HATAN;
			}
		}

		return isComplete ? SolutionState.COMPLETE : SolutionState.INCOMPLETE;
	}

	@Override
	protected List<HashikakeAssumption> assume(HashikakeBoard b, HashikakeTopology topology,
			HashikakeAssumption prevAssumption) {
		List<HashikakeAssumption> result = new ArrayList<HashikakeAssumption>();
		for (HashiState s : b.getHashiStates()) {
			if (!s.isFixed()) {
				for (int i = 0; i <= 2; i++) {
					if (s.isPossible(i)) {
						result.add(new HashikakeAssumption(s.def.i, i));
					}
				}
			}
		}
		return result;
	}

	@Override
	protected void applyAssumption(
			List<BoardSnapshot<HashikakeBoard, HashikakeTopology, HashikakeAssumption>> bss,
			HashikakeBoard b, HashikakeAssumption a) throws HatanException {
		HashiState s = b.getHashiState(a.i);
		s.setNumber(a.n);
		s.setAssumed();
	}

	@Override
	protected void excludeAssumption(HashikakeBoard b, HashikakeAssumption a) throws HatanException {
		b.getHashiState(a.i).setForbidden(a.n);
	}

	@Override
	protected boolean isAlreadyTried(
			List<BoardSnapshot<HashikakeBoard, HashikakeTopology, HashikakeAssumption>> bss,
			HashikakeBoard b, HashikakeAssumption a) {
		HashiState s = b.getHashiState(a.i);
		if (s.isFixed() || !s.isPossible(a.n)) {
			return true;
		}

		for (BoardSnapshot<HashikakeBoard, HashikakeTopology, HashikakeAssumption> bs : bss) {
			if (bs.getBoard().getHashiState(s.def.i).getNumber() == a.n) {
				return true;
			}
		}

		return false;
	}

	@Override
	protected void sortBoardSnapshots(
			List<BoardSnapshot<HashikakeBoard, HashikakeTopology, HashikakeAssumption>> bss) {
		// what can I do?
		return;
	}

}
